<?PHP
include_once("functions.php");//bootstrap functions

/**
* @desc Octopus - Bootstrapper & Class Discovery/Routing
*/
final class octopus extends object {
	/**
	* @desc application path
	* @var string
	*/
	static $appPath;
	/**
	* @desc framework path
	* @var string
	*/
	static $frameworkPath;
	
	/**
	* @desc web root path
	* @var string
	*/
	static $webRootPath;
	
	/**
	* @desc app relative path
	* @var string
	*/
	static $appRelativePath;
	
	/**
	* @desc Requested API
	* @var string
	*/
	public $api;
	
	/**
	* @desc Path to controller
	* @var string
	*/
	public $controllerPath;
	
	/**
	* @desc Controller name
	* @var string
	*/
	public $controllerName;
	
	/**
	* @desc Requested action
	* @var string
	*/
	public $controllerAction;
	
	/**
	* @desc Controller object
	* @var Controller
	*/
	public $controllerObject;
	
	/**
	* @desc private controllerArguments collection
	* @var array
	*/
	private $controllerArgs;
	
	final public function __construct($path) {
		$this->api = $path;
		$path = explode(".",$path);
		
		$this->controllerAction = $path[count($path)-1];
		$this->controllerName = $path[count($path)-2];
		unset($path[count($path)-1]);
		unset($path[count($path)-1]);
		$path = array_values($path);
		$path = implode(DS,$path);
		$path = ltrim(preg_replace("@([".preg_quote(DS)."]+)@sui",DS,$path),DS);
		$this->controllerPath = ltrim($path.DS.$this->controllerName,DS);
		if(file_exists($this->controllerPath.".php")) 
			include_once($this->controllerPath.".php");
		else throw new Exception_ControllerNotFound($this->api);
		
		//load annotations
		$controller = $this->controllerName."Controller";
		$this->controllerObject = new $controller();
		$this->controllerObject->path = $this->controllerPath;
		$this->controllerObject->controllerAction = $this->controllerAction;
		$this->controllerObject->controllerName = $this->controllerName;
		try {
			$annotation = new ReflectionAnnotatedMethod($controller,$this->controllerAction);
			$this->controllerObject->set("actionConfig",$annotation->getAnnotation("actionConfig"));
		} catch(ReflectionException $e) {
			//not found, maybe will be overloaded by __callAction
		}
	}
	
	public function exec() {
		//assign parameters
		if(method_exists($this->controllerObject,$this->controllerAction)) {				
				$ref = new ReflectionObject($this->controllerObject);
				$method = $ref->getMethod($this->controllerAction);
				$args = array();
				foreach($method->getParameters() as $i=>$param) {
					$paramName = $param->getName();
					if($param->isDefaultValueAvailable() && $this->get("element.".$paramName.".value") == null
					&& $this->get($paramName) == null && !isset($_REQUEST[$paramName])) {
						$args[$i] = $param->getDefaultValue();
					}
					else if($this->get("element.".$paramName.".value") != null)
						if($param->isPassedByReference() === true)
				   			$args[$i] = &$this->get("element.".$paramName.".value");
				   		else
							$args[$i] = $this->getram("element.".$paramName.".value");
					else if($this->get($paramName) != null)
						 $args[$i] = $this->get($paramName);
					else if(isset($_REQUEST[$paramName])) {
						$args[$i] = $_REQUEST[$paramName];
					}
					else if($param->isDefaultValueAvailable() === false) 
						 $args[$i] = null;
					else
						throw new SystemException("Not implemented ".__FILE__.":".__LINE__);
					//var_dump($param->getClass());
					if($param->getClass() != null) {
						$class = $param->getClass()->getName();
						$args[$i] = new $class($args[$i]);
					}
					$this->controllerArgs[$paramName] = $args[$i]; 
				} 
				return call_user_func_array(
					array(
						&$this->controllerObject, 
						$this->controllerAction
					),
					$args
				);
			}
			else if($this->controllerObject->actionExists($this->controllerAction)) {
				return $this->controllerObject->__callAction($this->controllerAction); 
			}
			else {
				throw new Exception_ActionNotFound($this->api);
			}
	}
	
	/**
	* @desc the main function called to init the framework
	*/
	static final function bootstrap() {
		//we love auto configuring, aint we ? :)
		self::$appPath = getcwd();
		self::$frameworkPath = dirname(__FILE__);
		self::$appRelativePath = "/".ltrim(fix_separator(dirname($_SERVER['SCRIPT_NAME'])),DS);
		self::$webRootPath =  dirname($_SERVER['SCRIPT_FILENAME']); //TODO: fix for Nginx
		
		//in case of web request
		if(isset($_GET['_REQUEST_PATH'])) {
			//TODO: Implement JSON/XMLRPC/WSDL's somewhere here
			$path = explode("?",$_GET['_REQUEST_PATH'],2);
			$path = str_replace("/".self::$appRelativePath,"",$path[0]);
			$path = preg_split("@[\.|/]@sui",$path);
			foreach($path as $idx=>$str)
				if(empty($str)) unset($path[$idx]);

			if(count($path) == 0) $path = array("index","index"); //default boot
			if(count($path) < 2) {
				//TODO: ...
				throw new Exception("Not implemented!");
			}
			
			$octopus = new octopus(implode(".",$path));
			header("X-Powered-by: EnterprisePHP Framework");
			header("X-Author: Lyubomir Petrov");
			header("X-Powered-by-url: http://lpetrov.mylo.bg/");
			$source = $octopus->exec();
			header('Content-Type: text/html; charset=utf-8');
			header("Content-Length: " . mb_strlen($source));
			if(
				$octopus->get("actionConfig") && 
				isset($octopus->get("actionConfig")->browserCache) &&
				$octopus->get("actionConfig")->browserCache === true
			) {
				header('Cache-Control: must-revalidate');
				header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $octopus->get("actionConfig")->browserCacheSecs) . ' GMT');
				header("Last-Modified: " . gmdate('D, d M Y H:i:s', time() ) . ' GMT');
			}
			echo $source;
		} else {
			throw new Exception("URI not found!");
		}
	}  
	/**
	* @desc init a new Octopus with path $path
	* @var $path uri
	* @return Octopus
	*/
	static final function load($path) {
		return new octopus($path);
	}
	public function &set($var,$val) {
		$this->controllerObject->set($var,$val);
		return $this;
	}
	public function get($var) {
		return $this->controllerObject->get($var);
	}
}
?>